﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary;

namespace SmartMathLibrary.Filters.BloomFilter
{
    /// <summary>
    /// This class represents a multiple BloomFilter. The difference between a normal BloomFilter and a multiple 
    /// BloomFilter is defined by the number of hash value checkings. A multiple BloomFilter creates and checks 
    /// more than one hash value.
    /// </summary>
    [Serializable]
    public class MultipleBloomFilter
    {
        /// <summary>
        /// This field holds the hash values.
        /// </summary>
        private readonly UInt64[] hashArrays;

        /// <summary>
        /// This field holds the number of checking instances.
        /// </summary>
        private readonly int checkingInstances;

        /// <summary>
        /// Initializes a new instance of the <see cref="MultipleBloomFilter"/> class.
        /// </summary>
        /// <param name="checkingInstances">The number checking instances, which the filter supports.</param>
        public MultipleBloomFilter(int checkingInstances)
        {
            if (checkingInstances < 0)
            {
                throw new ArgumentException("checkingInstances < 0", "checkingInstances");
            }

            this.checkingInstances = checkingInstances;
            this.hashArrays = new UInt64[checkingInstances];
        }

        /// <summary>
        /// Adds a new item to the filter.
        /// </summary>
        /// <param name="item">The item to add.</param>
        public void Add(IMultipleBloomFilter item)
        {
            if (item == (IMultipleBloomFilter) null)
            {
                throw new ArgumentNullException("item");
            }

            for (int i = 0; i < this.checkingInstances; i++)
            {
                UInt64 itemHash = item.GetBloomHashCode(i);

                for (int j = 0; j < 64; j++)
                {
                    if (BitOperations.IsBitSet(itemHash, j))
                    {
                        this.hashArrays[i] = BitOperations.SetBit(this.hashArrays[i], j);
                    }
                }
            }
        }

        /// <summary>
        /// Determines whether the virtual list contains the specified item.
        /// </summary>
        /// <param name="item">The item to check.</param>
        /// <returns>
        /// True if the virtual list contains the specified item otherwise, false.
        /// </returns>
        public bool Contains(IMultipleBloomFilter item)
        {
            if (item == (IMultipleBloomFilter) null)
            {
                throw new ArgumentNullException("item");
            }

            for (int i = 0; i < this.checkingInstances; i++)
            {
                UInt64 itemHash = item.GetBloomHashCode(i);

                for (int j = 0; j < 64; j++)
                {
                    if (BitOperations.IsBitSet(itemHash, j) && !(BitOperations.IsBitSet(this.hashArrays[i], j)))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
    }
}